package com.aurora.source.source;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.text.TextUtils;
import android.widget.Toast;

import com.aurora.source.util.LogUtils;
import com.aurora.source.source.activity.SplashActivity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * @author: Lance
 * @description: 1.收集错误信息 2.保存错误信息
 * @date: 2018/8/7 15:18
 * @version: 1.0
 */
public class CrashHandler implements Thread.UncaughtExceptionHandler {

    private static CrashHandler sInstance = null;
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    private Context mContext;
    // 保存手机信息和异常信息
    private Map<String, String> mMessage = new HashMap<>();

    public static CrashHandler getInstance() {
        if (sInstance == null) {
            synchronized (CrashHandler.class) {
                if (sInstance == null) {
                    synchronized (CrashHandler.class) {
                        sInstance = new CrashHandler();
                    }
                }
            }
        }
        return sInstance;
    }

    private CrashHandler() {
    }

    /**
     * 初始化默认异常捕获
     *
     * @param context context
     */
    public void init(Context context) {
        try {
            mContext = context;
            // 获取默认异常处理器
            mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
            // 将此类设为默认异常处理器
            Thread.setDefaultUncaughtExceptionHandler(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        try {
            if (!handleException(e)) {
                // 未经过人为处理,则调用系统默认处理异常,弹出系统强制关闭的对话框
//                if (mDefaultHandler != null) {
//                    mDefaultHandler.uncaughtException(t, e);
//                }
                showExceptionInActivity();
            } else {
                // 已经人为处理,系统自己退出
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                System.exit(1);
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 是否人为捕获异常
     *
     * @param e Throwable
     * @return true:已处理 false:未处理
     */
    private boolean handleException(Throwable e) {
        try {
            if (e == null) {// 异常是否为空
                return false;
            }
            new Thread() {// 在主线程中弹出提示
                @Override
                public void run() {
                    Looper.prepare();
                    Toast.makeText(mContext, "捕获到异常", Toast.LENGTH_SHORT).show();
                    Looper.loop();
                }
            }.start();
            collectErrorMessages();
            saveErrorMessages(e);
            LogUtils.i(String.valueOf(e));
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return false;
    }

    /**
     * 1.收集错误信息
     */
    private void collectErrorMessages() {
        PackageManager pm = mContext.getPackageManager();
        try {
            PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                String versionName = TextUtils.isEmpty(pi.versionName) ? "null" : pi.versionName;
                String versionCode = "" + pi.versionCode;
                mMessage.put("versionName", versionName);
                mMessage.put("versionCode", versionCode);
            }
            // 通过反射拿到错误信息
            Field[] fields = Build.class.getFields();
            if (fields != null && fields.length > 0) {
                for (Field field : fields) {
                    field.setAccessible(true);
                    try {
                        mMessage.put(field.getName(), field.get(null).toString());
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 2.保存错误信息
     *
     * @param e Throwable
     */
    private void saveErrorMessages(Throwable e) {
        try {
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> entry : mMessage.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                sb.append(key).append("=").append(value).append("\n");
            }
            Writer writer = new StringWriter();
            PrintWriter pw = new PrintWriter(writer);
            e.printStackTrace(pw);
            Throwable cause = e.getCause();
            // 循环取出Cause
            while (cause != null) {
                cause.printStackTrace(pw);
                cause = e.getCause();
            }
            pw.close();
            String result = writer.toString();
            sb.append(result);
            String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(new Date());
            String fileName = "crash-" + time + "-" + System.currentTimeMillis() + ".log";
            // 有无SD卡
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = SourceApplication.getInstance().getExternalFilesDir("CrashHandler").getAbsolutePath() + "/";
                File dir = new File(path);
                if (!dir.exists()) dir.mkdirs();
                FileOutputStream fos = null;
                try {
                    fos = new FileOutputStream(path + fileName);
                    fos.write(sb.toString().getBytes());
                } catch (Exception e1) {
                    e1.printStackTrace();
                } finally {
                    if (fos != null) {
                        try {
                            fos.close();
                        } catch (IOException e1) {
                            e1.printStackTrace();
                        }
                    }
                }
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 在用户自定义Activity中显示异常信息
     */
    private void showExceptionInActivity() {
        try {
            // 具体Activity调用者自己实现
            Intent intent = new Intent(mContext, SplashActivity.class);
            if (!(mContext instanceof Activity)) {
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            mContext.startActivity(intent);
            System.exit(1);// 关闭已奔溃的app进程
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
